package validator;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 映射jquery validation的验证方法
 * @author heantai
 *
 */
public class JqueryMethod {
	
	private final static Logger log=LoggerFactory.getLogger(JqueryMethod.class);
	
	private final static Map<String, Method> METHODS=new HashMap<String, Method>();
	protected final static Map<String, String> MESSAGES=new HashMap<String, String>();
	
	/**
	 * 指定自定义方法的类，并初始化自定义方法
	 * @param customeMethodClass
	 */
	public static void init(Class<?> customeMethodClass){
		if(customeMethodClass==null){
			customeMethodClass=JqueryMethod.class;
		}else{
			try {//加载Methods类，通过static{}设置默认提示信息
				Class.forName(customeMethodClass.getName());
			} catch (ClassNotFoundException e) {
			}
		}
		
		//初始自定义方法时会重复put JqueryMethod定义的方法
		Method[] ms=customeMethodClass.getMethods();
		for (int i = 0; i < ms.length; i++) {
			METHODS.put(ms[i].getName(), ms[i]);
		}
		
		MESSAGES.put(ACCEPT, "请选择后缀为 {0} 的文件");//请输入拥有合法后缀名的字符串
		MESSAGES.put(CREDITCARD, "请输入合法的信用卡号");
		MESSAGES.put(DATEISO, "请输入合法的日期 (ISO).");
		MESSAGES.put(DIGITS, "只能输入整数");
		MESSAGES.put(EMAIL, "请输入正确格式的电子邮件");
		MESSAGES.put(EQUALTO, "请再次输入相同的值");
		MESSAGES.put(MAX, "请输入一个最大为 {0} 的值");
		MESSAGES.put(MAXLENGTH, "请输入一个长度最多是 {0} 的字符串");
		MESSAGES.put(MIN, "请输入一个最小为 {0} 的值");
		MESSAGES.put(MINLENGTH, "请输入一个长度最少是 {0} 的字符串");
		MESSAGES.put(NUMBER, "请输入合法的数字");
		MESSAGES.put(RANGE, "请输入一个介于 {0} 和 {1} 之间的值");
		MESSAGES.put(REQUIED, "必选字段");
		MESSAGES.put(URL, "请输入合法的网址");
	}
	
	public static String getMsg(String rule){
		return MESSAGES.get(rule);
	}
	
	static{
		
		
		init(null);
		
		REGEX_EMAIL= "^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|" +
		"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|" +
		"\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|" +
		"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|" +
		"((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?" +
		"(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|" +
		"[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|" +
		"(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|" +
		"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*" +
		"(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@" +
		"((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
		"|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
		"([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
		"*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+" +
		"(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|" +
		"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|" +
		"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|" +
		"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?$";
		
		REGEX_URL="^(https?|ftp):\\/\\/(((([a-z]|\\d|-|\\.|_|~|" +
				"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|" +
				"(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?" +
				"(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\." +
				"(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\." +
				"(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\." +
				"(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|" +
				"((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|" +
				"(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
				"([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
				"*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+" +
				"(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|" +
				"(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])" +
				"([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*" +
				"([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?)(:\\d*)?)" +
				"(\\/((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|" +
				"(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|" +
				"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|" +
				"[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)?(\\?((([a-z]|\\d|-|\\.|_|~|" +
				"[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(%[\\da-f]{2})|" +
				"[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\uE000-\\uF8FF]|\\/|\\?)*)?" +
				"(\\#((([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|" +
				"(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?$";
	}
	private final static String REGEX_URL;
	
	private final static String REGEX_EMAIL;
	
	public final static String REQUIED="required";
	
	public final static String MAXLENGTH="maxlength";
	
	public final static String MINLENGTH="minlength";
	
	public final static String MIN="min";
	
	public final static String MAX="max";
	
	public final static String NUMBER="number";
	
	public final static String DIGITS="digits";
	
	public final static String EMAIL="email";
	
	public final static String URL="url";
	
	public final static String RANGE="range";
	
	public final static String CREDITCARD="creditcard";
	
	public final static String EQUALTO="equalTo";
	
	public final static String ACCEPT="accept";
	
	@Deprecated
	public final static String DATE="date";
	
	public final static String DATEISO="dateISO";
	
	public static boolean dateISO(Object value,String param){
		return stringRegexCheck(value, param, "^\\d{4}[\\/-]\\d{1,2}[\\/-]\\d{1,2}$");
	}
	
	/**
	 * return this.optional(element) || !/Invalid|NaN/.test(new Date(value)); <br/>
	 * 格式可以为yyyy-MM-dd或yyyy MM dd或yyyy-M-d或October 13, 1975 11:13:00或yyyy MM dd 11:13:00 <br/>
	 * 格式太过随意
	 */
	@Deprecated
	public static boolean date(Object value,String param){
		return dateISO(value, param);
	}
	
	public static void main(String[] args) {
		System.out.println(dateISO("2012-01-01", null));
	}
	
	public static boolean accept(Object value,String param){
		if(value!=null){
			value=value.toString().toLowerCase();
		}
		return stringRegexCheck(value, param, ".*("+param+")$");
	}
	
	
	public static boolean isXSS(String s){
		return false;
	}
	
	/**
	 * 
	 * @param value
	 * @param param	格式为#id，对后台来说id为需要对比的某个属性
	 * @return false
	 */
	public static boolean equalTo(Object value,String param){
		if(value==null&&param==null){
			return true;
		}
		
		if(value!=null&&param!=null&&value.toString().equals(param)){
			return true;
		}
		
		return false;
	}
	
	public static boolean creditcard(Object value,String param){
		if (!required(value, param)) {
			return true;
		}
		
		// accept only digits and dashes
		if(value.toString().matches("[^0-9-]+")){
			return false;
		}
		
		int nCheck = 0;
		boolean bEven = false;
		
		String v=value.toString().replaceAll("\\D", "");

		for (int n = v.length() - 1; n >= 0; n--) {
			int nDigit = new Integer(v.substring(n,n+1));
			if (bEven) {
				if ((nDigit *= 2) > 9)
					nDigit -= 9;
			}
			nCheck += nDigit;
			bEven = !bEven;
		}

		return (nCheck % 10) == 0;
	}
	
	public static boolean range(Object value,String param){
		String[] params=param.replace("[", "").replace("]", "").split(",");
		return min(value, params[0])&&max(value, params[1]);
	}
	
	private static boolean stringRegexCheck(Object value,String param,String regex){
		if (!required(value, param)) {
			return true;
		}

		return value.toString().matches(regex);
	}
	
	public static boolean url(Object value,String param){
		return stringRegexCheck(value, param, REGEX_URL);
	}
	
	public static boolean email(Object value, String param) {
		return stringRegexCheck(value, param, REGEX_EMAIL);
	}
	
	public static boolean digits(Object value,String param){
		return stringRegexCheck(value, param, "^\\d+$");
	}
	
	public static boolean number(Object value,String param){
		return stringRegexCheck(value, param, "^-?(?:\\d+|\\d{1,3}(?:,\\d{3})+)(?:\\.\\d+)?$");
	}
	
	public static boolean max(Object value,String param){
		if (!required(value, param)) {
			return true;
		}
		
		if(!number(value, null)){
			return false;
		}
		
		if(new Double(value.toString())>new Double(param)){
			return false;
		}
		
		return true;
	}
	
	
	public static boolean min(Object value,String param){
		if (!required(value, param)) {
			return true;
		}
		if(!number(value, null)){
			return false;
		}
		
		if(new Double(value.toString())<new Double(param)){
			return false;
		}
		
		return true;
	}
	
	public static boolean minlength(Object value, String param){
		if (!required(value, param)) {
			return true;
		}
		if(value.toString().length()<new Integer(param)){
			return false;
		}else{
			return true;
		}
	}
	
	
	/**使用反射调用，参数须保持一致*/
	public static boolean required(Object value,String param){
		if(value==null||value.toString().trim().isEmpty()){
			return false;
		}else{
			return true;
		}
	}

	public static boolean maxlength(Object value, String param){
		if(value==null||value.toString().length()<=new Integer(param)){
			return true;
		}else{
			return false;
		}
	}

	public static boolean validate(String rule, Object ruleParam, Object value) {
		log.debug("rule:{},ruleparam:{},value:{}",new Object[]{rule,ruleParam,value});
		if(!METHODS.containsKey(rule)){
			log.warn("验证方法[{}]不存在",rule);
			return true;
		}
		
		try {
			return (Boolean) METHODS.get(rule).invoke(null,value,ruleParam);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	//TODO remote ajax验证还是在页面上动态添加比较好，url需要动态
	//JqueryValidator.setFieldValidate(DEMO_ACTION_LOGIN_MAP, "name", JqueryMethod.REMOTE, JqueryMethod.remote(), "名字已经存在");
	public static String remote(String field,String url,String id){
		String s="{url:'"+url+"'," +
				"type:'post'," +
				"dataType:'json'," +
				"data:{" +
					"'"+field+"':function(){return $('#"+id+"')}" +
				"}}";
		return s;
	}
	class Remote{
		String url;
		private String type="'post'";
		private String dataType="'json'";
		//使用两个List<String>，一个存储key,一个存储值，增加addData(String,String)方法
		Map<String, String> data;
		
		
		@Override
		public String toString() {
			//返回格式为json{}
			/*
{
	url:'${pageContext.request.contextPath}/jsp/user/opencard!isBinded?'+new Date(),
	type:'post',
	dataType:'json',
	data:{
		'name':function(){return $('#name')}
	}
}
			 */
			return null;
		}
	}
	
}
